// ==== 基础类型
let isDone: boolean = false;
let nInt1: number = 0b010, nInt2: number = 0o777;
let string: string = `hahha_${isDone}`;
let list1: number[] = [1, 2, 3];
let list: Array<number> = [1, 2, 3];
let tuple: [string, number] = ['元组1', 1]; //元组
console.log(`tuple[0]:${tuple[0]}, tuple[1]: ${tuple[1]}`);
enum Color1 {Red, Green, Blue} // 枚举
enum Color2 {Red = 1, Green, Blue}
let notSure: any = 4; // Any
notSure = "change type";
// void  null和undefined  never

// ==== as
let someValue: any = "this is a string";
let strLength: number = (someValue as string).length;

// ==== 解构
let input = [1, 2];
let [first, second] = input;
console.log(`first: ${first}, second: ${second}`); // 1, 2

// ==== 展开
let first1 = [1, 2];
let second1 = [3, 4];
let bothPlus1 = [0, ...first1, ...second1, 5];
console.log(`bothPlus1: ${bothPlus1}`); // 1, 2, 3, 4, 5

// ==== 接口
interface Ittt {
  name: string,
  width?: number; //可选属性
  readonly zero: number; //只读属性
  getFullName(): string;
}
let ittt1: Ittt = {
  name: 'ittt',
  width: 10,
  zero: 0,
  getFullName() {
    return "omg";
  },
}
interface Ittt2 extends Ittt {
  say(content: string): string;
}
// ittt1.zero = 1; //不能通过编译
class ClassIttt implements Ittt2 { //接口可以继承
  name: string;
  zero: number;
  getFullName(): string {
      return "classIttt";
  }
  say(content: string): string {
      return "hi!";
  }
}
interface Ibbb1 {
  color: string;
}
interface Iccc extends Ibbb1, Ittt {
  long: number;
}

// ==== 类
// 公共，私有与受保护的修饰符
class Animal {
  // private私有
  private name: string;
  public ss: string = 'a';
  tt: string = 'c'; //默认public
  //protected修饰符与 private修饰符的行为很相似，
  // 但有一点不同， protected成员在派生类中仍然可以访问。
  protected uu: string = 'u';

  readonly rName: string; //只读属性
  static origin = {x: 0, y: 0}; // 静态属性

  constructor(theName: string) { 
    this.name = theName; 
    this.rName = 'abc';
  }
}
class CCat extends Animal {
  call(): string {
    // return `${this.name} is call`; //出错，编译不了
    return `${this.uu} is call`;
  }
}
// getter/setter
class Employee {
  private _fullName: string;

  get fullName(): string {
    return this._fullName;
  }

  set fullName(newName: string) {
    this._fullName = newName;
  }
}
// 抽象类
// 抽象类做为其它派生类的基类使用。 它们一般不会直接被实例化。 
// 不同于接口，抽象类可以包含成员的实现细节。
abstract class Box {
  abstract makeSound(): void; // 抽象方法

  move(): void {
    console.log('roaming the earch...');
  }
}

// ==== 函数
// 书写完整函数类型
let myAdd = function(x: number, y: number): number { return x + y; };
// 可选参数
// JavaScript里，每个参数都是可选的，可传可不传。 没传参的时候，它的值就是undefined。 
// 在TypeScript里我们可以在参数名旁使用 ?实现可选参数的功能。
function myReduce(firstName: string, lastName?: string) {
  if (lastName) {
    console.log(firstName + " " + lastName);
  } else {
    console.log(firstName);
  }
}
myReduce('lee');
myReduce('lee', 'chen');

// ==== 泛型
let numbers: Array<number> = [1, 2];
let numbers2: number[] = [3, 4];
// 泛型方法
function identity<T>(arg: T): T {
  return arg;
}
// 泛型类
class GenericNumber<T> {
  zeroValue: T;
  add: (x: T, y: T) => T;
}
let myGenericNumber = new GenericNumber<number>();
myGenericNumber.zeroValue = 0;
myGenericNumber.add = function(x, y) { return x + y; };
// 泛型约束
interface Lengthwise {
  length: number;
}
function loggingIdentity<T extends Lengthwise>(arg: T): T {
  console.log(arg.length);  // Now we know it has a .length property, so no more error
  return arg;
}

// ==== 枚举
enum Direction1 { //数值枚举
  Up = 1,
  Down,
  Left,
  Right
}
enum Direction2 { //字符串枚举
  Up = "UP",
  Down = "DOWN",
  Left = "LEFT",
  Right = "RIGHT",
}
enum Direction3 { //混合枚举
  Up = 1,
  Down = 2,
  Left = "LEFT",
  Right = "RIGHT",
}
const enum Enum { //常量枚举
  A = 1,
  B = A * 2
}

// ==== 类型兼容
interface Named {
  name: string;
}
class Named2 {
  name: string;
  call(): void {}
}
class Person {
  name: string;
}
let p: Named;
// 此处不会报错，因为Named和Person的结构
p = new Person();
// 此处不会报错，因为Named2的结构包含Named
p = new Named2();

let xxx = (a: number) => 0;
let yyy = (b: number, s: string) => 0;
yyy = xxx; // OK
// xxx = yyy; // Error

// ==== 联合类型
// 联合类型表示一个值可以是几种类型之一。 我们用竖线（ |）分隔每个类型
interface Bird {
  fly(): void;
  layEggs(): void;
}
interface Fish {
  swim(): void;
  layEggs(): void;
}
function getSmallPet(price :number): Fish | Bird {
  return price > 100 ?  {fly: () => {}, layEggs: () => {}} : {swim: () => {}, layEggs: () => {}};
}
// 类型保护
// 类型保护就是一些表达式，它们会在运行时检查以确保在某个作用域里的类型。
// pet is Fish就是类型谓词。 谓词为 parameterName is Type这种形式
// TypeScript会将变量缩减为那个具体的类型，只要这个类型与变量的原始类型是兼容的。
function isFish(pet: Fish | Bird): pet is Fish {
  return (<Fish>pet).swim !== undefined;
}

function isNumber(x: any): x is number {
  return typeof x === "number";
}
function isString(x: any): x is string {
  return typeof x === "string";
}
function padLeft1(value: string, padding: string | number) {
  if (isNumber(padding)) {
      return Array(padding + 1).join(" ") + value;
  }
  if (isString(padding)) {
      return padding + value;
  }
  throw new Error(`Expected string or number, got '${padding}'.`);
}
function padLeft2(value: string, padding: string | number) {
  if (typeof padding === "number") {
      return Array(padding + 1).join(" ") + value;
  }
  if (typeof padding === "string") {
      return padding + value;
  }
  throw new Error(`Expected string or number, got '${padding}'.`);
}

// ==== 交叉类型 &
// 交叉类型用于组合多个类型为一个类型。新的类型将具有所有组合类型的属性。
interface Person333 {
  name: string;
  age: number;
}
interface Employee333 {
  employeeId: number;
}
type PersonEmployee = Person333 & Employee333;
let personEmployee: PersonEmployee = {
  name: "Alice",
  age: 30,
  employeeId: 12345
};

// ==== 类型别名
type Name = string;
type NameResolver = () => string;
type NameOrResolver = Name | NameResolver;

// ==== keyof T
// 对于任何类型 T， keyof T的结果为 T上已知的公共属性名的联合
let personProps: keyof Person; // 等价于： let personProps: 'name' | 'age' | 'employeeId';


// ==== 命名空间
// https://www.tslang.cn/docs/handbook/namespaces.html
// https://www.tslang.cn/docs/handbook/namespaces-and-modules.html
namespace Validation {
  export interface StringValidator {
      isAcceptable(s: string): boolean;
  }

  const lettersRegexp = /^[A-Za-z]+$/;
  const numberRegexp = /^[0-9]+$/;

  export class LettersOnlyValidator implements StringValidator {
      isAcceptable(s: string) {
          return lettersRegexp.test(s);
      }
  }

  export class ZipCodeValidator implements StringValidator {
      isAcceptable(s: string) {
          return s.length === 5 && numberRegexp.test(s);
      }
  }
}

// Some samples to try
let strings = ["Hello", "98052", "101"];
// Validators to use
let validators: { [s: string]: Validation.StringValidator; } = {};
validators["ZIP code"] = new Validation.ZipCodeValidator();
validators["Letters only"] = new Validation.LettersOnlyValidator();
// Show whether each string passed each validator
for (let s of strings) {
  for (let name in validators) {
      console.log(`"${ s }" - ${ validators[name].isAcceptable(s) ? "matches" : "does not match" } ${ name }`);
  }
}


// 案例：typescript如何判断实例是否实现了接口？- 1
// 不能用instanceof，因为运行时不存在Interface
// ·TS 中判断是否实现接口的核心原则是基于结构而不是基于名称的。即鸭子类型判断。
// ·实现：
interface IA{
  discriminator: 'I-AM-IA';
  member:string;
}
function instanceOfIA(object: any) {
  return object.discriminator === 'I-AM-IA';
}
var ia:any = {discriminator: 'I-AM-IA', member:"foobar"};
console.log(`ia is IA: ${instanceOfIA(ia)}`);


// 案例：typescript如何判断实例是否实现了接口？- 2
// typescript如何判断实例是否实现了接口？
// 使用in关键字和keyof操作符来检查一个对象是否至少实现了某个接口的所有必需成员

// 不验证属性类型，仅验证属性存在性。
function hasProperties<T>(obj: any, props: (keyof T)[]): obj is T {
  return props.every(prop => prop in obj);
}
// 严格验证，类型检查来验证属性类型
function hasTypedProperties<T>(obj: any, props: { [K in keyof T]: (val: any) => boolean }): obj is T {
  return Object.keys(props).every(key => props[key as keyof T](obj[key]));
}

interface IBC {
  name: string;
  method1(): void;
}
const ibObj1 = {
  name: 'test',
  method1: () => console.log('method1 called')
};
const ibObj2 = {
  name: 123,
  method1: () => console.log('method1 called')
};
console.log(hasProperties<IBC>(ibObj1, ['name', 'method1'])); // true
console.log(hasProperties<IBC>(ibObj2, ['name', 'method1'])); // true
// 更严格的类型检查示例
console.log(hasTypedProperties<IBC>(ibObj1, {
  name: val => typeof val === 'string',
  method1: val => typeof val === 'function'
})); // true
console.log(hasTypedProperties<IBC>(ibObj2, {
  name: val => typeof val === 'string',
  method1: val => typeof val === 'function'
})); //false